<?php

namespace tests;

use ArrayObject;
use FastRoute\Dispatcher;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Webman\App;
use Webman\Config;
use Webman\Context;
use Webman\Http\Response;
use Webman\Http\Request;
use Webman\Route;

trait WebmanHttpTestTrait
{


    /**
     * 模拟GET请求
     * @param string $uri 请求路径
     * @param array $params
     * @param array $headers 请求头
     * @return Response
     */
    public function get(string $uri, array $params = [], array $headers = []): Response
    {
        return $this->request('GET', $uri, $params, $headers);
    }

    /**
     * 模拟POST请求
     * @param string $uri 请求路径
     * @param array $data 请求数据
     * @param array $headers 请求头
     * @return Response
     */
    public function post(string $uri, array $data = [], array $headers = []): Response
    {
        return $this->request('POST', $uri, $data, $headers);
    }

    /**
     * 模拟POST JSON请求
     * @param string $uri 请求路径
     * @param array $data 请求数据
     * @param array $headers 请求头
     * @return Response
     */
    public function postJson(string $uri, array $data = [], array $headers = []): Response
    {
        $headers['Content-Type'] = 'application/json';
        $response = $this->request('POST', $uri, $data, $headers);
        $response->rawBody(json_decode($response->rawBody(), true));
        return $response;
    }

    /**
     * 模拟请求
     * @param string $method 请求方法
     * @param string $uri 请求路径
     * @param array $data 请求数据
     * @param array $headers 请求头
     * @return Response
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws \ReflectionException
     */
    public function request(string $method, string $uri, array $data = [], array $headers = []): Response
    {
        $method = strtoupper($method);
        $requestTarget = $uri;
        $body = '';
        $headers = $headers ?: [];
        $headers['Host'] = $headers['Host'] ?? 'localhost';

        if ($method === 'GET') {
            $parsed = parse_url($uri);
            $existingQuery = $parsed['query'] ?? '';
            $extraQuery = http_build_query($data);
            if ($existingQuery !== '' && $extraQuery !== '') {
                $combinedQuery = $existingQuery . '&' . $extraQuery;
            } else {
                $combinedQuery = $existingQuery ?: $extraQuery;
            }
            $path = $parsed['path'] ?? $uri;
            $requestTarget = $path . ($combinedQuery !== '' ? '?' . $combinedQuery : '');
            $headers['Content-Type'] = $headers['Content-Type'] ?? 'application/x-www-form-urlencoded';
        } else {
            $contentType = $headers['Content-Type'] ?? 'application/x-www-form-urlencoded';
            if (stripos($contentType, 'application/json') !== false) {
                $body = json_encode($data);
                $headers['Content-Type'] = 'application/json';
            } else {
                $body = http_build_query($data);
                $headers['Content-Type'] = 'application/x-www-form-urlencoded';
            }
        }

        $head = sprintf("%s %s HTTP/1.1\r\n", $method, $requestTarget);
        foreach ($headers as $name => $value) {
            $head .= sprintf("%s: %s\r\n", $name, $value);
        }
        $raw = $head . "\r\n" . $body;

        $requestClassName = Config::get('app.request_class', Request::class);
        $request = new $requestClassName($raw);
        Context::reset(new ArrayObject([Request::class => $request]));

        // 调用Webman路由分发逻辑处理请求
        $routeInfo = Route::dispatch($request->method(), $request->path());
        if ($routeInfo[0] === Dispatcher::FOUND) {
            $routeInfo[0] = 'route';
            $callback = $routeInfo[1]['callback'];
            $route = clone $routeInfo[1]['route'];
            $app = $controller = $action = '';
            $args = !empty($routeInfo[2]) ? $routeInfo[2] : [];
            if ($args) {
                $route->setParams($args);
            }
            if (is_array($callback)) {
                $controller = $callback[0];
                $plugin = App::getPluginByClass($controller);
                $app = self::getAppByController($controller);
                $action = self::getRealMethod($controller, $callback[1]) ?? '';
            } else {
                $plugin = App::getPluginByPath($request->path());
            }
            $callback = App::getCallback($plugin, $app, $callback, $args, true, $route);
            $request->plugin = $plugin;
            $request->app = $app;
            $request->controller = $controller;
            $request->action = $action;
            $request->route = $route;
            $response = $callback($request);
            if (!$response instanceof Response) {
                if (!is_string($response)) {
                    $response = self::stringify($response);
                }
                $response = new Response(200, [], $response);
            }
            return $response;
        }
        $status = $routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED ? 405 : 404;

        // 默认控制器路由解析（参考 App::onMessage）
        $controllerAndAction = self::parseControllerActionForTest($request->path());
        $pluginFromPath = App::getPluginByPath($request->path());
        if (!$controllerAndAction ||
            Route::isDefaultRouteDisabled($pluginFromPath, $controllerAndAction['app'] ?: '*') ||
            Route::isDefaultRouteDisabled($controllerAndAction['controller']) ||
            Route::isDefaultRouteDisabled([$controllerAndAction['controller'], $controllerAndAction['action']])
        ) {
            return new Response($status);
        }

        $plugin = $controllerAndAction['plugin'] ?? $pluginFromPath;
        $app = $controllerAndAction['app'] ?? '';
        $controller = $controllerAndAction['controller'];
        $action = $controllerAndAction['action'];

        $callback = App::getCallback($plugin, $app, [$controller, $action], [], true, null);
        $request->plugin = $plugin;
        $request->app = $app;
        $request->controller = $controller;
        $request->action = $action;
        $request->route = null;

        $response = $callback($request);
        if (!$response instanceof Response) {
            if (!is_string($response)) {
                $response = self::stringify($response);
            }
            $response = new Response(200, [], $response);
        }
        return $response;
    }

    public static function getAppByController(string $controllerClass): string
    {
        $controllerClass = trim($controllerClass, '\\');
        $tmp = explode('\\', $controllerClass, 5);
        $pos = $tmp[0] === 'plugin' ? 3 : 1;
        if (!isset($tmp[$pos])) {
            return '';
        }
        return strtolower($tmp[$pos]) === 'controller' ? '' : $tmp[$pos];
    }

    /**
     * GetRealMethod.
     * @param string $class
     * @param string $method
     * @return string
     */
    protected static function getRealMethod(string $class, string $method): string
    {
        $method = strtolower($method);
        $methods = get_class_methods($class);
        foreach ($methods as $candidate) {
            if (strtolower($candidate) === $method) {
                return $candidate;
            }
        }
        return $method;
    }


    private static function stringify($data): string
    {
        $type = gettype($data);
        switch ($type) {
            case 'boolean':
                return $data ? 'true' : 'false';
            case 'NULL':
                return 'NULL';
            case 'array':
                return 'Array';
            case 'object':
                if (!method_exists($data, '__toString')) {
                    return 'Object';
                }
            default:
                return (string)$data;
        }
    }

    /**
     * 基于 App::parseControllerAction 的测试版解析器
     */
    private static function parseControllerActionForTest(string $path): false|array
    {
        $path = str_replace(['-', '//'], ['', '/'], $path);
        $pathExplode = explode('/', trim($path, '/'));
        $isPlugin = isset($pathExplode[1]) && $pathExplode[0] === 'app';
        $configPrefix = $isPlugin ? "plugin.$pathExplode[1]." : '';
        $pathPrefix = $isPlugin ? "/app/$pathExplode[1]" : '';
        $classPrefix = $isPlugin ? "plugin\\$pathExplode[1]" : '';
        $suffix = Config::get("{$configPrefix}app.controller_suffix", '');
        if ($suffix === '') {
            $suffix = 'Controller';
        }
        $relativePath = trim(substr($path, strlen($pathPrefix)), '/');
        $pathExplode = $relativePath ? explode('/', $relativePath) : [];

        $action = 'index';
        if (!$controllerAction = self::guessControllerActionForTest($pathExplode, $action, $suffix, $classPrefix)) {
            if (count($pathExplode) <= 1) {
                return false;
            }
            $action = end($pathExplode);
            unset($pathExplode[count($pathExplode) - 1]);
            $controllerAction = self::guessControllerActionForTest($pathExplode, $action, $suffix, $classPrefix);
        }
        return $controllerAction;
    }

    /**
     * 基于 App::guessControllerAction 的测试版
     */
    private static function guessControllerActionForTest(array $pathExplode, string $action, string $suffix, string $classPrefix): false|array
    {
        $map = [];
        $map[] = trim("$classPrefix\\app\\controller\\" . implode('\\', $pathExplode), '\\');
        foreach ($pathExplode as $index => $section) {
            $tmp = $pathExplode;
            array_splice($tmp, $index, 1, [$section, 'controller']);
            $map[] = trim("$classPrefix\\" . implode('\\', array_merge(['app'], $tmp)), '\\');
        }
        foreach ($map as $item) {
            $map[] = $item . '\\index';
        }
        foreach ($map as $controllerClass) {
            if (substr($controllerClass, -11) === '\\controller') {
                continue;
            }
            $controllerClass .= $suffix;
            if ($controllerAction = self::getControllerActionForTest($controllerClass, $action)) {
                return $controllerAction;
            }
        }
        return false;
    }

    /**
     * 基于 App::getControllerAction 的测试版
     */
    private static function getControllerActionForTest(string $controllerClass, string $action): false|array
    {
        if (str_starts_with($action, '__')) {
            return false;
        }
        if ($controller = self::getControllerForTest($controllerClass)) {
            if ($method = self::getActionForTest($controller, $action)) {
                return [
                    'plugin' => App::getPluginByClass($controller),
                    'app' => self::getAppByController($controller),
                    'controller' => $controller,
                    'action' => $method
                ];
            }
        }
        return false;
    }

    /**
     * 基于 App::getController 的测试版
     */
    private static function getControllerForTest(string $controllerClass): false|string
    {
        if (class_exists($controllerClass)) {
            return new \ReflectionClass($controllerClass)->name;
        }
        return false;
    }

    /**
     * 基于 App::getAction 的测试版
     */
    private static function getActionForTest(string $controllerClass, string $action): false|string
    {
        $methods = get_class_methods($controllerClass);
        $lowerAction = strtolower($action);
        foreach ($methods as $candidate) {
            if (strtolower($candidate) === $lowerAction) {
                return $candidate;
            }
        }
        if (method_exists($controllerClass, $action)) {
            return false;
        }
        if (method_exists($controllerClass, '__call')) {
            return $action;
        }
        return false;
    }
}
